iT邦幫忙

2024 iThome 鐵人賽

DAY 6
0

在本節中,我們將深入了解量化投資中至關重要的部分——回測。透過回測,我們可以在歷史數據上測試交易策略的有效性,評估其風險和收益特性。我們將介紹兩個流行的Python回測框架:BacktraderZipline,並學習如何使用它們來進行策略回測。那因為這兩個中 Backtrader 可以方便安裝在 Colab 上但是 Zipline 不行等等問題, 所以我們還是依照 Backtrader 為主, 至於 Zipline 則放在 Appendix-2 裡,完整 Zipline 程式可直接到 Appendix 2-H 去看與 Backtrader 做比較。
今日 Colab


一、為什麼需要回測

回測(Backtesting)是指在歷史數據上測試交易策略,以評估其在過去的表現。透過回測,我們可以:

  • 評估策略的可行性:確認策略是否具有盈利能力。
  • 理解策略的風險特性:包括波動性、最大回撤等。
  • 優化策略參數:調整策略中的參數以達到最佳性能。
  • 避免過擬合:透過在不同的歷史時期進行測試,確保策略的穩健性。

二、Python回測框架

1. Backtrader

  • 簡介
    Backtrader是一個功能強大且靈活的Python回測框架,支持多種資產類別、指標和執行模式。
  • 特點
    • 支持多資料源和多資產回測。
    • 內置大量的技術指標和分析工具。
    • 支持實時數據和紙上交易(Paper Trading, 也就是模擬交易)。

2. Zipline

  • 簡介
    Zipline是Quantopian開發的Python回測引擎,專為構建和評估量化交易策略而設計。
  • 特點
    • 支持日級別和分鐘級別的數據回測。
    • 提供豐富的金融數據處理功能。
    • 與Pandas和NumPy緊密集成。

三、使用Backtrader進行回測

1. 安裝Backtrader

在Colab中安裝Backtrader:

!pip install backtrader

2. 建立策略類

在Backtrader中,您需要繼承bt.Strategy類,並實現策略的邏輯。

import backtrader as bt

class SmaCrossStrategy(bt.Strategy):
    params = dict(
        sma_short_period=10,
        sma_long_period=50,
    )

    def __init__(self):
        self.sma_short = bt.ind.SMA(period=self.p.sma_short_period)
        self.sma_long = bt.ind.SMA(period=self.p.sma_long_period)
        self.crossover = bt.ind.CrossOver(self.sma_short, self.sma_long)

    def next(self):
        if not self.position:
            if self.crossover > 0:
                self.buy()
        elif self.crossover < 0:
            self.sell()

3. 獲取數據

使用yfinance獲取數據,並轉換為Backtrader的數據格式。

import yfinance as yf
import pandas as pd

data = yf.download('AAPL', start='2020-01-01', end='2021-01-01')
data_bt = bt.feeds.PandasData(dataname=data)

4. 設置回測環境並執行

cerebro = bt.Cerebro()
cerebro.addstrategy(SmaCrossStrategy)
cerebro.adddata(data_bt)
cerebro.broker.setcash(100000.0)
cerebro.run()

5. 結果分析

打印最終資產淨值:

print(f'最終資產淨值: {cerebro.broker.getvalue():.2f}')

繪製回測結果:

Case 1.如果在 local machine python terminal 之上:

cerebro.plot()

cerebro.plot()
Case 2.如果在 Colab 之上,以下筆者試過比較方便顯示結果圖:
#https://www.roelpeters.be/how-to-use-backtrader-plots-in-python-notebooks/
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = [15, 12]
plt.rcParams.update({'font.size': 12}) 
cerebro.plot(iplot = False)

四、回測結果解讀

1. 重要指標

  • 累計收益率:策略在回測期間的總收益率。
  • 年化收益率:將總收益率換算為年化數值。
  • 最大回撤:回測期間資產淨值最大的下降幅度。
  • 夏普比率:衡量策略的風險調整後收益。

2. 分析方法

  • 與基準比較:將策略的表現與市場指數進行比較。
  • 查看交易明細:分析每筆交易的盈虧情況。
  • 時間序列圖表:觀察資產淨值隨時間的變化。

五、回測中的注意事項

1. 資料質量

  • 確保使用高質量的歷史數據,避免數據錯誤影響回測結果。

2. 滑點和交易成本

  • 在回測中考慮交易成本,如手續費和滑點,以模擬真實交易環境。
cerebro.broker.setcommission(commission=0.001)  # 設置手續費為0.1%
cerebro.broker.set_slippage_perc(0.001)         # 設置滑點為0.1%

3. 過擬合風險

  • 避免過度調整策略參數,以免在未來市場中表現不佳。

4. 交易邏輯的合理性

  • 確保策略符合市場邏輯,不依賴偶然的數據模式。

六、進一步探索

1. 添加性能分析

使用Backtrader的分析器(Analyzer)獲取更多的性能指標,例如以下我們加入 Sharpe 值與 Drawdown 值。

cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')

完整程式:

import backtrader as bt
import yfinance as yf

# 定義策略
class SmaCrossStrategy(bt.Strategy):
    params = dict(
        sma_short_period=10,
        sma_long_period=50,
    )

    def __init__(self):
        self.sma_short = bt.ind.SMA(period=self.p.sma_short_period)
        self.sma_long = bt.ind.SMA(period=self.p.sma_long_period)
        self.crossover = bt.ind.CrossOver(self.sma_short, self.sma_long)

    def next(self):
        if not self.position:
            if self.crossover > 0:
                self.buy()
        elif self.crossover < 0:
            self.close()

# 獲取數據
data = yf.download('AAPL', start='2020-01-01', end='2021-01-01')
data_bt = bt.feeds.PandasData(dataname=data)

# 設置回測環境
cerebro = bt.Cerebro()
cerebro.addstrategy(SmaCrossStrategy)
cerebro.adddata(data_bt)
cerebro.broker.setcash(100000.0)
cerebro.broker.setcommission(commission=0.001)
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe',
                    timeframe=bt.TimeFrame.Days, annualize=True, riskfreerate=0.01)
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trade_analyzer')

# 執行回測
results = cerebro.run()
sharpe = results[0].analyzers.sharpe.get_analysis()
drawdown = results[0].analyzers.drawdown.get_analysis()
trade_analyzer = results[0].analyzers.trade_analyzer.get_analysis()

# 打印結果
print(f"最終資產淨值: {cerebro.broker.getvalue():.2f}")
if sharpe.get('sharperatio') is not None:
    print(f"夏普比率: {sharpe['sharperatio']:.2f}")
else:
    print("夏普比率無法計算,請檢查策略和數據。")

print(f"最大回撤: {drawdown.max.drawdown:.2f}%")
print(f"總交易次數: {trade_analyzer.total.closed}")

# 繪製圖表
## 如果在 local machine python terminal 之上:
cerebro.plot()

## 如果在 Colab 之上:
#https://www.roelpeters.be/how-to-use-backtrader-plots-in-python-notebooks/
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = [15, 12]
plt.rcParams.update({'font.size': 12}) 
cerebro.plot(iplot = False)

可以得到下面圖形:
https://ithelp.ithome.com.tw/upload/images/20240920/20120549GZ8231jLGn.png

整個圖形的解讀有非常多細節可以看,我們這裡先簡單來看:
1.可以在最下面的折線圖看到我們使用的策略在何時買以及何時賣
2.中間層可以看到我們買一次清倉揪竟是賺還是陪
https://ithelp.ithome.com.tw/upload/images/20240920/20120549R0nMG9Uxic.jpg

2. 多策略比較

  • 在同一回測環境中其實我們也以同時測試多個策略,比較它們的表現。這一點會在未來的篇章去介紹!

3. 實時數據交易

  • 這一點非常重要,其實 Backtrader 是支持連接真實且實時數據去做交易的,目前這個功能我們等在多了解量化交易再去做,不然可能會被當韭菜割><。

八、總結

在本節中,我們:

  • 了解了回測的重要性,以及它在策略開發中的角色。
  • 學習了如何使用Backtrader進行回測,包括數據導入、策略實現和結果分析。
  • 討論了回測中的關鍵注意事項,如交易成本、過擬合和資料質量。

透過回測,我們能夠在進入實際交易之前評估策略的有效性。接下來,我們將探索如何引入AI技術,提升交易策略的智能化水平。


Appendix 1.作業:

  1. 嘗試不同的策略參數:修改均線的週期(如20日、50日),觀察對回測結果的影響。
  2. 添加性能指標:使用Backtrader的分析器,計算策略的年化收益率、夏普比率和最大回撤。
  3. 實現其他策略:嘗試實現基於其他技術指標(如RSI、MACD)的策略,並進行回測。這個未來我們也會 demo 給大家看看

透過實踐,您將更深入地理解回測框架的使用方法,為開發高效的交易策略奠定基礎。

Appendix 2.Zipline:


注意:Zipline在Colab中可能不易安裝,且支持有限。如果您希望使用Zipline,建議在本地環境或支持的環境中進行。這裡附上 Zipline 的官網
那因為在 Colab 上實在不方便用 Zipline, 筆者在這裡就簡單代一下如何使用, 有興趣的讀者歡迎在自己的 PC 上嘗試看看:

Appendix 2-A、Zipline簡介

Appendix 2-A-1. Zipline的特點

  • 專業性強:Zipline是Quantopian開發的開源回測引擎,設計用於構建和評估量化交易策略。
  • 豐富的功能
    • 支持日級別和分鐘級別的數據回測。
    • 內置大量的金融數據處理和分析工具。
    • 提供了與Pandas和NumPy的緊密集成。

Appendix 2-A-2. 使用限制

  • 環境要求:Zipline對Python版本和依賴庫有特定要求,通常在Python 3.5及以下版本上運行最佳。
  • 數據源:默認使用內置的歷史數據,也可以使用自定義的數據源。

Appendix 2-B、Zipline的安裝

Appendix 2-B-1. 建議的安裝環境

  • 操作系統:建議使用macOS或Linux系統,Windows上安裝可能會遇到更多問題。
  • Python版本:建議使用Python 3.5 (也可嘗試至3.7版本。

Appendix 2-B-2. 建立虛擬環境

為了避免與現有的Python環境產生衝突,建議建立一個新的虛擬環境。

使用conda建立虛擬環境:

conda create -n zipline_env python=3.5
conda activate zipline_env

Appendix 2-B-3. 安裝Zipline

使用conda安裝:

conda install -c quantopian zipline

注意:如果遇到安裝問題,可以嘗試以下命令:

conda install -c conda-forge zipline

Appendix 2-C、使用Zipline進行回測

Appendix 2-C-1. 匯入必要的庫

from zipline.api import order, symbol, record, set_benchmark
from zipline import run_algorithm
import pandas as pd
import pytz
from datetime import datetime

Appendix 2-C-2. 定義策略

我們一樣採取簡單的均線交叉策略供讀者好與 Backtrader 做比較
示例:簡單的均線交叉策略

def initialize(context):
    context.asset = symbol('AAPL')
    context.short_window = 10
    context.long_window = 50
    context.history_window = context.long_window + 1
    set_benchmark(context.asset)

def handle_data(context, data):
    # 獲取歷史價格數據
    price_history = data.history(context.asset, 'price', bar_count=context.history_window, frequency='1d')

    # 計算移動平均線
    short_mavg = price_history[-context.short_window:].mean()
    long_mavg = price_history[-context.long_window:].mean()

    # 獲取當前持倉
    current_position = context.portfolio.positions[context.asset].amount

    # 產生交易信號
    if short_mavg > long_mavg and current_position == 0:
        order(context.asset, 100)  # 買入100股
    elif short_mavg < long_mavg and current_position > 0:
        order(context.asset, -100)  # 賣出持有的股票

    # 記錄數據
    record(AAPL=data.current(context.asset, 'price'),
           short_mavg=short_mavg,
           long_mavg=long_mavg)

Appendix 2-C-3. 設置回測參數

start = pd.Timestamp('2020-01-01', tz='utc')
end = pd.Timestamp('2021-01-01', tz='utc')

Appendix 2-C-4. 執行回測

results = run_algorithm(start=start,
                        end=end,
                        initialize=initialize,
                        handle_data=handle_data,
                        capital_base=100000,
                        data_frequency='daily',
                        bundle='quantopian-quandl')

Appendix 2-D、自訂數據源

Zipline默認使用內置的數據包(bundle),如quantopian-quandl。但我們也可以使用自訂的數據源,例如從Yahoo Finance下載的數據。

Appendix 2-D-1. 安裝yahoo-direct數據包

pip install zipline-data-bundles

Appendix 2-D-2. 注冊數據包

在命令行中運行:

zipline ingest -b yahoo-direct

Appendix 2-D-3. 在回測中使用自訂數據包

results = run_algorithm(start=start,
                        end=end,
                        initialize=initialize,
                        handle_data=handle_data,
                        capital_base=100000,
                        data_frequency='daily',
                        bundle='yahoo-direct')

Appendix 2-E、結果分析

Appendix 2-E-1. 查看回測結果

# 查看資產淨值曲線
import matplotlib.pyplot as plt

plt.figure(figsize=(12,6))
results.portfolio_value.plot()
plt.title('資產淨值曲線')
plt.show()

Appendix 2-E-2. 分析策略績效

from pyfolio import create_full_tear_sheet

# 將回測結果轉換為日度收益率
returns = results.portfolio_value.pct_change().dropna()

# 使用PyFolio生成策略報告
create_full_tear_sheet(returns)

注意:需要安裝PyFolio庫

pip install pyfolio

Appendix 2-F、回測中的注意事項

Appendix 2-F-1. 時區問題

確保時間戳使用UTC時區,否則可能導致數據對齊問題。

start = pd.Timestamp('2020-01-01', tz='utc')
end = pd.Timestamp('2021-01-01', tz='utc')

Appendix 2-F-2. 資產符號

使用symbol()函數指定交易的資產。例如,symbol('AAPL')

Appendix 2-F-3. 數據頻率

data_frequency參數可以設定為dailyminute,取決於數據的時間間隔。

Appendix 2-F-4. 資產池

如果需要交易多個資產,可以在initialize函數中定義資產列表,並在handle_data中迭代處理。


Appendix 2-G、完整範例

以下是整合上述步驟的完整代碼:

from zipline.api import order, symbol, record, set_benchmark
from zipline import run_algorithm
import pandas as pd
import pytz
from datetime import datetime
import matplotlib.pyplot as plt

def initialize(context):
    context.asset = symbol('AAPL')
    context.short_window = 10
    context.long_window = 50
    context.history_window = context.long_window + 1
    set_benchmark(context.asset)

def handle_data(context, data):
    # 獲取歷史價格數據
    price_history = data.history(context.asset, 'price', bar_count=context.history_window, frequency='1d')

    # 計算移動平均線
    short_mavg = price_history[-context.short_window:].mean()
    long_mavg = price_history[-context.long_window:].mean()

    # 獲取當前持倉
    current_position = context.portfolio.positions[context.asset].amount

    # 產生交易信號
    if short_mavg > long_mavg and current_position == 0:
        order(context.asset, 100)  # 買入100股
    elif short_mavg < long_mavg and current_position > 0:
        order(context.asset, -100)  # 賣出持有的股票

    # 記錄數據
    record(AAPL=data.current(context.asset, 'price'),
           short_mavg=short_mavg,
           long_mavg=long_mavg)

# 設置回測期間
start = pd.Timestamp('2020-01-01', tz='utc')
end = pd.Timestamp('2021-01-01', tz='utc')

# 執行回測
results = run_algorithm(start=start,
                        end=end,
                        initialize=initialize,
                        handle_data=handle_data,
                        capital_base=100000,
                        data_frequency='daily',
                        bundle='quantopian-quandl')

# 繪製資產淨值曲線
plt.figure(figsize=(12,6))
results.portfolio_value.plot()
plt.title('資產淨值曲線')
plt.show()

Appendix 2-H、Zipline的限制與替代方案

Appendix 2-H-1. 安裝困難

  • Windows支援有限:在Windows上安裝Zipline可能會遇到許多問題。
  • Python版本限制:Zipline對Python版本有嚴格要求,較新版本的Python可能不支援。

Appendix 2-H-2. 數據包過時

  • Quantopian關閉:Quantopian已於2020年關閉,其提供的數據包可能不再更新。

Appendix 2-H-3. 替代方案

  • Backtrader:功能強大,社區活躍,支持Python 3.6以上版本。
  • PyAlgoTrade:支持事件驅動的回測和實時交易。

Ref:
1.Backtrader官方文件https://www.backtrader.com/docu/
2.https://www.roelpeters.be/how-to-use-backtrader-plots-in-python-notebooks/
3.https://github.com/quantopian/zipline


上一篇
Day4:金融指標計算與策略開發基礎
下一篇
Day6:動量策略、均值回歸策略與技術指標策略開發
系列文
打開就會 AI 與數據分析的投資理財術30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
jessie1226
iT邦新手 5 級 ‧ 2024-11-22 18:18:38

版主您好,由於最近在學量化交易,看了您的文章後獲益良多,但在運行您的代碼時發生了以下問題
AttributeError Traceback (most recent call last)
in <cell line: 5>()
3 cerebro.adddata(data_bt)
4 cerebro.broker.setcash(100000.0)
----> 5 results = cerebro.run()

4 frames
/usr/local/lib/python3.10/dist-packages/backtrader/feeds/pandafeed.py in (.0)
210 # Transform names (valid for .ix) into indices (good for .iloc)
211 if self.p.nocase:
--> 212 colnames = [x.lower() for x in self.p.dataname.columns.values]
213 else:
214 colnames = [x for x in self.p.dataname.columns.values]

AttributeError: 'tuple' object has no attribute 'lower'
請問這是正常的嗎,如果不適該如何解決呢,謝謝

zivzhong iT邦新手 3 級 ‧ 2024-11-23 04:58:44 檢舉

jessie1226你好,我判斷了一下應該是 Yfinance 版本的問題,簡單改安裝成下面版本應該就不會有這個問題:
!pip install yfinance==0.2.44
另外因為寫作時程過於緊湊因此這份教程可能有一些遺落,後續時間允許下我也會進行補充跟改寫完整。最後感謝你對本教程的賞識!

好的,謝謝版主的指導!

我要留言

立即登入留言